﻿using System;
using System.Drawing;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace IsreversI.GameLogic
{
    /// <summary>
    /// Controller to be used for playing moves, starting a new game, etc.
    /// </summary>
    class GameStateController : BoardModelAttachment
    {
        #region member variables
        private GameStateView stateView;

        /// <summary>
        /// Set to true when the previous player had to skip a turn due to not having any moves available.
        /// </summary>
        private bool PlayerHadNoMoves = false;
        #endregion

        #region events
        public event BoardEventHandler FailedPlay;
        #endregion

        #region properties
        #endregion

        #region constructors
        public GameStateController(BoardModel model)
        {
            this.stateView = new GameStateView(model);
            this.stateView.PostBoardModelChanged += this.acceptNewModel;
            this.PreBoardModelChanged += this.stateView.acceptNewModel;
            this.stateView.PlayerHasNoMoves += this.handleNoMoves;
            this.handleModelChange(stateView.BoardModel);
        }

        public GameStateController(GameStateView stateView)
        {
            this.stateView = stateView;
            this.stateView.PostBoardModelChanged += this.acceptNewModel;
            this.PreBoardModelChanged += this.stateView.acceptNewModel;
            this.stateView.PlayerHasNoMoves += this.handleNoMoves;
            this.handleModelChange(stateView.BoardModel);
        }
        #endregion

        #region event handlers
        protected override void handleModelChange(BoardModel model)
        {
            base.handleModelChange(model);
            this.PlayerHadNoMoves = false;
        }

        /// <summary>
        /// Fired in response to the GameStateView reporting that the current player has no moves.
        /// If the previous player did have a move, this skips a turn (previous player can play again),
        /// otherwise the game is set to be over.
        /// </summary>
        public void handleNoMoves(object sender, BoardEventArgs bea)
        {
            // Hang until the model is locked.  May be dangerous, but we have to do this operation no matter what.
            bea.BoardModel.TryLockModel(-1);
            try
            {
                if (this.PlayerHadNoMoves)
                    bea.BoardModel.CurrentState = TurnState.GameOver;
                else
                    bea.BoardModel.CurrentState = bea.BoardModel.CurrentState.Flipped();
                this.PlayerHadNoMoves = true;
            }
            finally
            {
                bea.BoardModel.UnlockModel();
            }
        }
        #endregion

        #region public methods
        /// <summary>
        /// Try playing at the specified location.  In case of failure, the
        /// FailedPlay event is raised.
        /// </summary>
        public void PlayMove(Point position)
        {
            if (!this.BoardModel.TryLockModel(0))
                return;
            try
            {
                if (!this.stateView.LegalMove(position))
                {   // If the move isn't legal, let's inform everyone of it and get out.
                    BoardEventArgs bea = new BoardEventArgs(this.BoardModel);
                    bea.Point = position;
                    if (this.FailedPlay != null)
                        FailedPlay(this, bea);
                    return;
                }
                foreach (IntVector2 dir in BoardModel.Directions)
                {
                    int distance = this.stateView.DistanceToNextPointInDirectionThatIs(position, dir, (TileState)this.BoardModel.CurrentState);
                    if (distance < 2)
                        continue;
                    if (this.stateView.NextNPointsInDirectionAre(position, distance - 1, dir, (TileState)~this.BoardModel.CurrentState))
                        this.FlipNextNInDirection(position, distance - 1, dir);
                }
                this.BoardModel.PlaceStoneAt(position, (PlayerColour)this.BoardModel.CurrentState);
                this.BoardModel.CurrentState = this.BoardModel.CurrentState.Flipped();
                this.PlayerHadNoMoves = false;
            }
            finally
            {
                this.BoardModel.UnlockModel();
            }
        }
        #endregion

        #region private methods
        public void FlipNextNInDirection(Point origin, int distance, IntVector2 direction)
        {
            Point current = origin + direction;
            while (distance-- > 0)
            {
                this.BoardModel.FlipPieceAt(current);
                current += direction;
            }
        }
        #endregion
    }
}
